//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mail;
using CommunityServer.Configuration;

namespace CommunityServer.Components
{
	/// <summary>
	/// Summary description for EmailTemplateProvider.
	/// </summary>
	public abstract class EmailTemplateProvider
	{
		public static readonly string EmailTemplateProviderName = "EmailTemplateProvider";

		#region Instance

		private static EmailTemplateProvider _defaultInstance = null;

		static EmailTemplateProvider()
		{
			CreateDefaultCommonProvider();
		}

		/// <summary>
		/// Returns an instance of the user-specified data provider class.
		/// </summary>
		/// <returns>An instance of the user-specified data provider class.  This class must inherit the
		/// CommonDataProvider interface.</returns>
		public static EmailTemplateProvider Instance() 
		{
			return _defaultInstance;
		}

		public static EmailTemplateProvider Instance(Provider dataProvider) 
		{
			EmailTemplateProvider fdp = CSCache.Get(dataProvider.Name) as EmailTemplateProvider;
			if(fdp == null)
			{
				fdp = DataProviders.Invoke(dataProvider) as EmailTemplateProvider;
				CSCache.Max(dataProvider.Name,fdp);
			}
			return fdp;
		}

		/// <summary>
		/// Creates the Default CommonDataProvider
		/// </summary>
		private static void CreateDefaultCommonProvider()
		{
			// Get the names of the providers
			//
			CSConfiguration config = CSConfiguration.GetConfig();

			// Read the configuration specific information
			// for this provider
			//
			Provider dataProvider = (Provider) config.Providers[EmailTemplateProviderName];

			//Get the type
			Type type  = Type.GetType(dataProvider.Type);

			object newObject = null;
			if(type != null)
				newObject = Activator.CreateInstance(type);
            
			if(newObject == null) //If we can not create an instance, throw an exception
				ProviderException(dataProvider.Name);

			_defaultInstance = newObject as EmailTemplateProvider;
		}

		#region Exception
		private static void ProviderException(string providerName)
		{
			CSConfiguration config = CSConfiguration.GetConfig();
			HttpContext context = HttpContext.Current;
			if (context != null) 
			{
                    
				// We can't load the dataprovider
				//
				StreamReader reader = new StreamReader( context.Server.MapPath("~/Languages/" + config.DefaultLanguage + "/errors/DataProvider.htm") );
				string html = reader.ReadToEnd();
				reader.Close();

				html = html.Replace("[DATAPROVIDERCLASS]", providerName);
				html = html.Replace("[DATAPROVIDERASSEMBLY]", providerName);
				context.Response.Write(html);
				context.Response.End();
			} 
			else 
			{
				throw new CSException(CSExceptionType.DataProvider, "Unable to load " + providerName);
			}
		}
		#endregion

		#endregion

		public abstract MailMessage GetTemplate(string emailType, User userEmailTo);

		public static void PopulateHeaders(MailMessage email, string to, string[] cc, string[] bcc, bool html)
		{
			if (html) 
			{
				email.BodyFormat = MailFormat.Html;
				email.Body = "<html><body>" + FormatPlainTextAsHtml(email.Body).Trim() + "</body></html>";	
			}

			// Set to:
			//
			if(to != null)
				email.To = to;
			if(cc != null)
				email.Cc = Transforms.ToDelimitedString(cc, ",");
			if(bcc != null)
				email.Bcc = Transforms.ToDelimitedString(bcc, ",");
		}

		public static void PopulatePostFields(MailMessage email, Post post)
		{
			email.Body = Regex.Replace(email.Body, "\\[postedby\\]", post.Username.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			email.Body = Regex.Replace(email.Body, "\\[subject\\]", post.Subject.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			email.Body = Regex.Replace(email.Body, "\\[postdate\\]", post.User.GetTimezone(post.PostDate).ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			email.Body = Regex.Replace(email.Body, "\\[replyurl\\]", Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			email.Body = Regex.Replace(email.Body, "\\[moderatePostUrl\\]", Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			CSContext cntx = CSContext.Current;
			if(cntx.IsWebRequest)
				email.Body = Regex.Replace(email.Body, "\\[posturl\\]", Globals.HostPath(cntx.Context.Request.Url) + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			else
				email.Body = Regex.Replace(email.Body, "\\[posturl\\]", Globals.HostPath(new Uri(cntx.RawUrl)) + Globals.GetSiteUrls().Post(post.PostID), RegexOptions.IgnoreCase | RegexOptions.Compiled);

			if(post.Section != null)
				email.Body = Regex.Replace(email.Body, "\\[forumname\\]", post.Section.Name.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);

			//stringToFormat = Regex.Replace(stringToFormat, "\\[forumUrl\\]", Globals.HostPath(CSContext.Current.Context.Request.Url) + ForumUrls.Instance().Forum(post.SectionID), RegexOptions.IgnoreCase | RegexOptions.Compiled);
				
			// make urls clickable before adding post HTML
			if(email.BodyFormat == MailFormat.Html) email.Body = Regex.Replace(email.Body,@"(http|ftp|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:\$/~\+#]*[\w\-\@?^=%&/~\+#])?","<a href=\"$0\">$0</a>",RegexOptions.IgnoreCase | RegexOptions.Compiled);
				
			// strip html from post if necessary
			string postbody = post.RenderedBody(PostTarget.Other);				

			// if the user doesn't want HTML and the post is HTML, then strip it
			if ((email.BodyFormat != MailFormat.Html) && post.PostType == PostContentType.HTML) 
				postbody = FormatHtmlAsPlainText(postbody);
				
				// if the user wants HTML and the post is PlainText, then add HTML to it
			else if ((email.BodyFormat == MailFormat.Html) &&  post.PostType == PostContentType.Text) 
				postbody = FormatPlainTextAsHtml(postbody);

			// Finally, trim this post so the user doesn't get a huge email
			//
			postbody.Trim();

			email.Body = Regex.Replace(email.Body, "\\[postbody\\]", postbody, RegexOptions.IgnoreCase | RegexOptions.Compiled);
		}

		public static void PopulateUserFields(MailMessage email, User user)
		{
			email.From = FormatUserFields(email.From, user);
			email.Subject = FormatUserFields(email.Subject, user);
			email.Body = FormatUserFields(email.Body, user);
		}

		protected static string FormatUserFields(string stringToFormat, User user)
		{
			stringToFormat = Regex.Replace(stringToFormat, "\\[username\\]", user.Username.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = Regex.Replace(stringToFormat, "\\[email\\]", user.Email.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = Regex.Replace(stringToFormat, "\\[publicemail\\]", user.Profile.PublicEmail.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = Regex.Replace(stringToFormat, "\\[datecreated\\]", user.GetTimezone(user.DateCreated).ToString(user.Profile.DateFormat), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = Regex.Replace(stringToFormat, "\\[lastlogin\\]", user.GetTimezone(user.LastLogin).ToString(user.Profile.DateFormat), RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = Regex.Replace(stringToFormat, "\\[profileurl\\]", Globals.GetSiteUrls().UserEditProfile, RegexOptions.IgnoreCase | RegexOptions.Compiled);

			if (user.Password != null)
				stringToFormat = Regex.Replace(stringToFormat, "\\[password\\]", user.Password.Trim(), RegexOptions.IgnoreCase | RegexOptions.Compiled);

			return stringToFormat;
		}

		#region Protected Helpers

		protected static string FormatPlainTextAsHtml(string stringToFormat) 
		{
			if (stringToFormat == null || stringToFormat == string.Empty) return "";
			
			// line breaks
			stringToFormat = Regex.Replace(stringToFormat,"\n","<br />",RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = stringToFormat.Replace("\r", string.Empty);

			// make urls clickable
			stringToFormat = Regex.Replace(stringToFormat,@"(http|ftp|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#\$]*[\w\-\@?^=%&/~\+#])?","<a href=\"$0\">$0</a>",RegexOptions.IgnoreCase | RegexOptions.Compiled);

			return stringToFormat;
		}

		protected static string FormatHtmlAsPlainText(string stringToFormat) 
		{
			if (stringToFormat == null || stringToFormat == string.Empty) return "";
			
			// get rid of extra line breaks
			stringToFormat = Regex.Replace(stringToFormat,"\n"," ",RegexOptions.IgnoreCase | RegexOptions.Compiled);
			stringToFormat = stringToFormat.Replace("\r", string.Empty);
			
			// add linebreaks from HTML for <br>, <p>, <li>, and <blockquote> tags
			stringToFormat = Regex.Replace(stringToFormat,@"</?(br|p|li|blockquote)(\s/)?>","\n",RegexOptions.IgnoreCase | RegexOptions.Compiled);
			
			// strip all remaining HTML
			stringToFormat = Regex.Replace(stringToFormat,@"</?(\w+)(\s*\w*\s*=\s*(""[^""]*""|'[^']'|[^>]*))*|/?>","",RegexOptions.IgnoreCase | RegexOptions.Compiled);

			// replace special characters
			stringToFormat = stringToFormat.Replace("&nbsp;", " ");
			stringToFormat = stringToFormat.Replace("&lt;", "<");
			stringToFormat = stringToFormat.Replace("&gt;", ">");
			stringToFormat = stringToFormat.Replace("&amp;", "&");
			stringToFormat = stringToFormat.Replace("&quot;", "\"");

			return stringToFormat;
		}

		#endregion

	}
}
